package indi.zhifa.recipe.bailan.framework.redis.filter;

import indi.zhifa.recipe.bailan.framework.common.entity.exception.ServiceException;
import indi.zhifa.recipe.bailan.framework.redis.annotations.RedissonLock;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@AllArgsConstructor
@Aspect
@Slf4j
@Component
public class RedissonHandler {
    private RedissonClient mRedissonClient;

    @Around("@annotation(pRedissonConfig)")
    public Object around(ProceedingJoinPoint joinPoint, RedissonLock pRedissonConfig) throws Throwable {
        String prefix = pRedissonConfig.prefix();

        List<String> keys = new ArrayList<>();
        for(String keyExp : pRedissonConfig.keys()){
            String key = prefix+doKey(joinPoint,keyExp);
            keys.add(key);
        }
        List<RLock> rLocks = new ArrayList<>();
        for(String key : keys){
            RLock rLock = mRedissonClient.getLock(key);
            rLocks.add(rLock);
        }

        for(int i=0;i<keys.size();i++){
            RLock rLock = rLocks.get(i);
            String key = keys.get(i);
            if(pRedissonConfig.leaseTime() > 0){
                // 不加看门狗
                try{
                    if(pRedissonConfig.waitTime() > 0L){
                        boolean ret = rLock.tryLock(pRedissonConfig.waitTime(), pRedissonConfig.leaseTime(), TimeUnit.SECONDS);
                        if(!ret){
                            throw new ServiceException(key+" 上锁失败，请联系管理员");
                        }
                    }else{
                        rLock.lock(pRedissonConfig.leaseTime(),TimeUnit.SECONDS);
                    }

                }catch (InterruptedException interruptedException){
                    throw new ServiceException(key+" 上锁失败，失败信息是 "+interruptedException+"请联系管理员");
                }

            }else{
                // 加看门狗
                if(pRedissonConfig.waitTime() > 0L){
                    boolean ret = rLock.tryLock(pRedissonConfig.waitTime(), TimeUnit.SECONDS);
                    if(!ret){
                        throw new ServiceException(key+" 上锁失败，请联系管理员");
                    }
                }else{
                    rLock.lock();
                }
            }
        }

        Object ret = null;
        try{
            ret = joinPoint.proceed(joinPoint.getArgs());
        }catch (Exception ex){
            throw ex;
        }finally {
            try{
                for(RLock rLock : rLocks){
                    rLock.unlock();
                }
            }catch (Exception ex){
                log.error("解锁超时");
            }
        }
        return ret;
    }

    public String doKey(ProceedingJoinPoint joinPoint, String key) {
        //获取方法的参数名和参数值
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        List<String> paramNameList = Arrays.asList(methodSignature.getParameterNames());
        List<Object> paramList = Arrays.asList(joinPoint.getArgs());

        //将方法的参数名和参数值一一对应的放入上下文中
        EvaluationContext ctx = new StandardEvaluationContext();
        for (int i = 0; i < paramNameList.size(); i++) {
            ctx.setVariable(paramNameList.get(i), paramList.get(i));
        }

        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        // 解析SpEL表达式获取结果
        String value = spelExpressionParser.parseExpression(key).getValue(ctx).toString();
        return value;
    }
}
